package com.witdata.framework.service.code.impl;

import com.mybatisflex.core.query.QueryWrapper;
import com.witdata.mysql.base.service.CodeContentBaseService;
import com.witdata.mysql.base.service.UserCodeBaseService;
import com.witdata.common.domain.Status;
import com.witdata.common.domain.docker.Container;
import com.witdata.common.domain.dto.*;
import com.witdata.mysql.domain.entity.TCodeContent;
import com.witdata.mysql.domain.entity.TUserCode;
import com.witdata.common.exception.GlobalException;
import com.witdata.common.mapper.CodeMapper;
import com.witdata.common.util.IdUtils;
import com.witdata.framework.service.code.CodeService;
import com.witdata.framework.service.code.EnvironmentService;
import com.witdata.framework.service.docker.ContainerService;
import com.witdata.framework.service.docker.ExecuteService;
import com.witdata.rabbit.service.InstanceService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
@RequiredArgsConstructor
public class CodeServiceImpl implements CodeService {
    private final CodeContentBaseService codeContentBaseService;
    private final UserCodeBaseService userCodeBaseService;
    private final EnvironmentService environmentService;
    private final InstanceService instanceService;
    private final ContainerService containerService;
    private final ExecuteService executeService;

    @Override
    public void createCodeEnvironment(CodeDto codeDto) {
        InstanceDto instance = codeDto.getInstance();
        if (ObjectUtils.isEmpty(instance)) throw new GlobalException(Status.INSTANCEISNOTEXISTS);
        EnvironmentDto environment = codeDto.getEnvironment();
        if (ObjectUtils.isEmpty(environment)) throw new GlobalException(Status.ENVIRONMENTNOTEXISTS);
        Container container = containerService.createContainer(instance.getId(), environment.getImageName());
        TUserCode tUserCode = TUserCode.builder()
                .id(IdUtils.createId())
                .name(codeDto.getName())
                .userId(codeDto.getUser().getId())
                .instanceId(instance.getId())
                .environmentId(environment.getId())
                .containerId(container.getId())
                .build();
        userCodeBaseService.save(tUserCode);
    }

    @Override
    public List<CodeDto> getUserCodeList(Long userId) {
        List<TUserCode> tUserCodeList =
                userCodeBaseService.list(
                        new QueryWrapper().eq("user_id", userId)
                );
        List<CodeDto> codeDtoList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(tUserCodeList)) {
            tUserCodeList.forEach(tUserCode -> {
                EnvironmentDto environmentDto = environmentService.getEnvironmentById(tUserCode.getEnvironmentId());
                InstanceDto instanceDto = instanceService.getInstanceById(tUserCode.getInstanceId());
                if (ObjectUtils.isEmpty(instanceDto)) return;
                try {
                    Container containerStatus = containerService.getContainerStatus(instanceDto.getId(), tUserCode.getContainerId());
                    codeDtoList.add(CodeDto.builder()
                            .id(tUserCode.getId())
                            .containerId(containerStatus.getId())
                            .name(tUserCode.getName())
                            .environment(environmentDto)
                            .instance(instanceDto)
                            .status(containerStatus.getStatus())
                            .build());
                } catch (GlobalException ignored) {
                }
            });
        }
        return codeDtoList;
    }

    @Override
    public void runCode(CodeContentDto codeContentDto) {
        TUserCode tUserCode = userCodeBaseService.getById(codeContentDto.getCodeId());
        executeService.startExecute(ExecuteDto.builder()
                        .instanceId(tUserCode.getInstanceId())
                        .executeId(codeContentDto.getExecuteId())
                .build());
        EnvironmentDto environmentDto = environmentService.getEnvironmentById(tUserCode.getEnvironmentId());
        String prefixCommand = StringUtils.hasText(environmentDto.getPrefixCommand()) ? environmentDto.getPrefixCommand() : "";
        String suffixCommand = StringUtils.hasText(environmentDto.getSuffixCommand()) ? environmentDto.getSuffixCommand() : "";
        codeContentDto.setContent(prefixCommand + '\n' + codeContentDto.getContent()  + '\n' + suffixCommand);
        executeService.sendCommand(ExecuteDto.builder()
                        .instanceId(tUserCode.getInstanceId())
                        .executeId(codeContentDto.getExecuteId())
                        .command(codeContentDto.getContent())
                .build());
    }

    @Override
    public EnvironmentDto getEnvironmentByCodeId(Long codeId) {
        TUserCode tUserCode = userCodeBaseService.getById(codeId);
        return environmentService.getEnvironmentById(tUserCode.getEnvironmentId());
    }

    @Override
    public CodeDto getCodeInfoByCodeId(Long codeId) {
        TUserCode tUserCode = userCodeBaseService.getById(codeId);
        CodeDto codeDto = CodeMapper.INSTANCE.toCodeDto(tUserCode);
        InstanceDto instanceDto = instanceService.getInstanceById(tUserCode.getInstanceId());
        codeDto.setInstance(instanceDto);
        return codeDto;
    }

    @Override
    public void saveContent(CodeContentDto codeContentDto) {
        TCodeContent tCodeContent = codeContentBaseService.getOne(new QueryWrapper().eq("code_id", codeContentDto.getCodeId()));
        if (ObjectUtils.isEmpty(tCodeContent)) {
            tCodeContent = TCodeContent.builder()
                    .id(IdUtils.createId())
                    .codeId(codeContentDto.getCodeId())
                    .content(codeContentDto.getContent())
                    .created(LocalDateTime.now())
                    .updated(LocalDateTime.now())
                    .build();
            codeContentBaseService.save(tCodeContent);
        } else {
            tCodeContent.setContent(codeContentDto.getContent());
            codeContentBaseService.updateById(tCodeContent);
        }
    }

    @Override
    public CodeContentDto getCodeContent(Long codeId) {
        TCodeContent tCodeContent = codeContentBaseService.getOne(new QueryWrapper().eq("code_id", codeId));
        return CodeMapper.INSTANCE.toCodeContentDto(tCodeContent);
    }

    @Override
    public void stopCode(CodeContentDto codeContentDto) {
        TUserCode tUserCode = userCodeBaseService.getById(codeContentDto.getCodeId());
        containerService.stopContainer(InstanceContainerDto.builder()
                        .instanceId(tUserCode.getInstanceId())
                        .containerId(tUserCode.getContainerId())
                .build());
    }
}
